// Bachelor of Software Engineering 
// Media Design School 
// Auckland
// New Zealand
// 
// (c) 2005 - 2014 Media Design School 
// 
// File Name : Tablleau.cpp
// Description : Tablleau implementation
// Author : Serge Radinovich
// Mail : srad015@hotmail.com

#include "Tableau.h"
#include "Game.h"

////////////////////////////
//Constructor
///////////////////////////
Tableau::Tableau(std::list<Card*>* _Cards28)
{
	
	//Add 7 lists to Tableau
	for(int i =0; i <7; i++)
	{
		m_Stacks.push_back(new std::list<Card*>);
	}
	//Populate the lists with new cards
	auto cardIter = _Cards28->begin();
	for(int i = 0; i < 7; i++)
	{
		for(int j = 0; j <= i; j++)
		{
			m_Stacks[i]->push_back(new Card(*(*cardIter)));
			delete *cardIter;
			cardIter = _Cards28->erase(cardIter);
		}
	}

	//Set null spaces
	for(int i = 0; i < 7; i++)
	{
		m_NullSpaces.push_back(GameObject(static_cast<float>(s_kiLeft + i*s_kiSpaceBetweenPiles), static_cast<float>(s_kiTop), Card::s_kiWidth, Card::s_kiHeight));
	}

	//Set Positions
	//Set bottom cards to be in line horizontally
	for(int pile = 0; pile < 7; pile++)
	{
		
		int _iLeftEdge = s_kiLeft + pile*s_kiSpaceBetweenPiles;
		m_Stacks[pile]->back()->SetPos(_iLeftEdge, s_kiTop);
		m_iLeftEdgeOfPile[pile] = _iLeftEdge;
		//Set each subsequent card in pile to be below it
		int iYPosMult = 0;
		for(auto card = m_Stacks[pile]->begin(); card != m_Stacks[pile]->end(); card++)
		{
			(*card)->SetPos(m_Stacks[pile]->back()->GetPos().x, s_kiTop + iYPosMult*s_kiSpaceBetweenCards);			
			iYPosMult++;
		}
		
		//Set top cards face up
		m_Stacks[pile]->back()->Flip();
	}
}


////////////////////////////
//Destructor
///////////////////////////
Tableau::~Tableau(void)
{

}

////////////////////////////
//Render every card in Tableau. If no card, render null space
///////////////////////////
void Tableau::Draw()
{
	for(int pile = 0; pile < 7; pile++)
	{
		if(!m_Stacks[pile]->empty())
		{
			for(auto card = m_Stacks[pile]->begin(); card != m_Stacks[pile]->end(); card++)
			{
				(*card)->Draw();
			}
		}
		else
		{
			//Null spaces
			Game* _pGame = Game::GetGameInstance();
			BitBlt(_pGame->GetBackBuffer()->getDC(), m_iLeftEdgeOfPile[pile], s_kiTop, Card::s_kiWidth, Card::s_kiHeight, m_hNullSpaceDC, 0, 0, SRCAND);
		}
	}
}

////////////////////////////
//Called whenever player tries to grab a card from tableau pile
//Returns the pile that cards taken from and adds to a list of grabbed cards
///////////////////////////
std::list<Card*>* Tableau::AttemptCardGrab(Vector2 _clickPos, std::list<Card*>* grabbedCards)
{
	//Take cards
	if(grabbedCards->empty())
	{	
		for(int pile = 0; pile < 7; pile++)
		{
			for(auto card = m_Stacks[pile]->begin(); card != m_Stacks[pile]->end(); card++)
			{
				//Can only grab cards that are face up
				if((*card)->IsFaceUp())
				{
					//Card is top so grab the single card
					if((*card) == m_Stacks[pile]->back())
					{
						if(CheckCollision(_clickPos, *card))
						{
							grabbedCards->push_back(*card);
							m_Stacks[pile]->pop_back();
							return m_Stacks[pile];
						}
					}
					else if( CheckCollision(_clickPos, *card, false)) //Check collision for card not on top of pile
					{
						//Iterate backwards from front card (back of list)				
						auto card2 = m_Stacks[pile]->end();
						card2--;
						//Grab every card until the one that was clicked is iterated to
						while(card2 != card)
						{
							grabbedCards->push_front(*card2);
							m_Stacks[pile]->pop_back();
							card2 = m_Stacks[pile]->end();
							card2--;
						}
						//Grab card clicked on
						grabbedCards->push_front(*card2);
						m_Stacks[pile]->pop_back();
						return m_Stacks[pile];
					}
				}
			}
		}
	}
	return nullptr;
}

////////////////////////////
//Called whenever player tries to deposit a card to tableau pile
//Returns true if cards were successfully deposited to tableau pile
///////////////////////////
bool Tableau::AttemptCardDeposit(Vector2 _clickPos, std::list<Card*>* _pGrabbedCards,  std::list<Card*>* &_pListDepositedTo)
{
	
	for(int pile = 0; pile < static_cast<int>(m_Stacks.size()); pile++)
	{
		//Check every empty pile for collisions
		if(m_Stacks[pile]->empty())
		{
			if(CheckCollision(_pGrabbedCards->front(), &m_NullSpaces[pile]))
				{
					
					if(ValidCardDeposit(_pGrabbedCards->front(), pile))
					{
						//Animate
						CardPileGroup::Animate(_pGrabbedCards, m_NullSpaces[pile].GetPos());
						
						//Dispense all cards grabbed into pile collided with
						while(!_pGrabbedCards->empty())
						{
							m_Stacks[pile]->push_back(_pGrabbedCards->front());
							_pGrabbedCards->pop_front();
						}
						ArrangeCardPositions();
						//Return list deposited to
						_pListDepositedTo = m_Stacks[pile];
						return true;
					}
					else
					{
						return false;
					}
				}
		}
		

		//Non-empty piles, error check so not too many cards can be put on screen
		if(m_Stacks[pile]->size() + _pGrabbedCards->size() > 13)
		{
			return false;
		}
	
		//Check every non-empty pile for collisions
		//Set first card to null space position
		auto _card = m_Stacks[pile]->begin();

		while(_card != m_Stacks[pile]->end())
		{
			//If the card checked is face up and top of pile
			if((*_card)->IsFaceUp() && *(*_card) == *m_Stacks[pile]->back())
			{
				if(CheckCollision(*_card, _pGrabbedCards->front()))//(CheckCollision(_clickPos, (*_card)))
				{
					
					if(ValidCardDeposit(_pGrabbedCards->front(), pile))
					{
						//Animate
						Vector2 _targetPos = m_Stacks[pile]->back()->GetPos();
						_targetPos.y += Tableau::s_kiSpaceBetweenCards;
						CardPileGroup::Animate(_pGrabbedCards, _targetPos);

						while(!_pGrabbedCards->empty())
						{
							m_Stacks[pile]->push_back(_pGrabbedCards->front());
							_pGrabbedCards->pop_front();
						}
						ArrangeCardPositions();
						//Return list deposited to
						_pListDepositedTo = m_Stacks[pile];
						return true;
					}
					else
					{
						return false;
					}
				}
			}
			_card++;
		}
	}	
	return false;
}

////////////////////////////
//Returns true if card can be deposited to Tableau pile according to game rules
///////////////////////////
bool Tableau::ValidCardDeposit(const Card* const _pCard, const int _iPile) const
{
	
	//For debugging!
	//return true; //THIS INVALIDATES RULES
	
	//Only kings can be added to empty piles
	if(m_Stacks[_iPile]->empty())
	{
		if(_pCard->GetRank() == KING)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	//Check rules
	if(m_Stacks[_iPile]->back()->GetColour() != _pCard->GetColour() )
	{
		if(m_Stacks[_iPile]->back()->GetRank() == _pCard->GetRank() + 1)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		return false;
	}
}

////////////////////////////
//Set all positions for cards in tableau piles
///////////////////////////
void Tableau::ArrangeCardPositions()
{
	
	for(int pile = 0; pile < static_cast<int>(m_Stacks.size()); pile++)
	{
		if(m_Stacks[pile]->empty())
		{
			continue;
		}
		//Set first card to null space position
		auto _card = m_Stacks[pile]->begin();
		(*_card)->SetPos(m_NullSpaces[pile].GetPos().x, m_NullSpaces[pile].GetPos().y);


		auto _prevCard = _card;
		_card++;
		int iYPosMult = 1;
		while(_card != m_Stacks[pile]->end())
		{
			(*_card)->SetPos((*_prevCard)->GetPos().x, (*_prevCard)->GetPos().y + iYPosMult*s_kiSpaceBetweenCards);
			
			iYPosMult++;
			_card++;
		}
	}
}

////////////////////////////
//Called whenever a top card of tableau pile needs to be flipped
//E.g. when a card is grabbed from a pile or when move undone
///////////////////////////
void Tableau::FlipTopCards()
{
	//Check all piles
	for(int pile = 0; pile < static_cast<int>(m_Stacks.size()); pile++)
	{
		if(m_Stacks[pile]->empty())
		{
			continue;
		}
		
		
		if(!m_Stacks[pile]->back()->IsFaceUp())
		{
			m_Stacks[pile]->back()->Flip();
			return;
		}
	}
}


////////////////////////////
//Undo a move made in relation to the tableau
///////////////////////////
void Tableau::UndoMove(Move* _move)
{
	//Check whther move involved flipping a card
	if(_move->m_bFlip && !_move->m_pPileMovedFrom->empty())
	{
		_move->m_pPileMovedFrom->back()->Flip();
	}

	while(!_move->m_pCardsMoved->empty())
	{
		_move->m_pPileMovedFrom->push_back(_move->m_pCardsMoved->back());
		_move->m_pCardsMoved->pop_back();
	}
	
	return;
}

////////////////////////////
//Animation at the start of every game
///////////////////////////
void Tableau::InitializeAnimate(CardPileGroup* _pStock)
{
	Game* _pGame = Game::GetGameInstance();
	
	
	//Grab final positions
	std::vector<std::list<Vector2>> _FinalPositions;
	for(int i = 0; i <7; i++)
	{
		_FinalPositions.push_back(std::list<Vector2>());
		auto _cardIter = m_Stacks[i]->begin();
		while(_cardIter != m_Stacks[i]->end())
		{
			_FinalPositions[i].push_back((*_cardIter)->GetPos());
			(*_cardIter)->SetPos(10,50); //NullPos from Stock.h
			_cardIter++;
		}
	}

	//Animate
	for(int i = 0; i < 7; i++)
	{		
		auto _cardIter = m_Stacks[i]->begin();
		auto _posIter = _FinalPositions[i].begin();
		while(_cardIter != m_Stacks[i]->end())
		{
			//Animate each card individually
			Vector2 _Increment = (*_posIter) - (*_cardIter)->GetPos();
			_Increment /= 20;
			for(int j = 0; j < 20; j++)
			{								
				//(*_cardIter)->UnDraw();
				_pGame->GetBackBuffer()->Clear();
				_pGame->DrawNullSpaces();
				//_pStock->GetStacks()[0]->back()->Draw();
				(*_cardIter)->Move(_Increment);	
				(*_cardIter)->Draw();

				for(int k = 0; k < 7; k++)
				{		
					auto _cardRedrawIter = m_Stacks[k]->begin();
					while(_cardRedrawIter != m_Stacks[k]->end())
					{
						if((*_cardRedrawIter)->GetPos().y >= 214)
						{
							(*_cardRedrawIter)->Draw();						
						}
						_cardRedrawIter++;
					}
				}

				_pGame->GetBackBuffer()->Present();
				Sleep(10);

			}
				(*_cardIter)->UnDraw();
				(*_cardIter)->SetPos(_posIter->x, _posIter->y);
				(*_cardIter)->Draw();
				_cardIter++;
				_posIter++;
			
		}
	}
}